RDSのパスワードをSecrets Managerで暗号化するCloudFormationテンプレートを作成してみた
はじめに
こんにちは、おのやんです。
みなさん、RDSのパスワードは暗号化できていますか?私はできていませんでした(超絶懺悔)。
CloudFormationのテンプレートにて、RDSの設定を記述する場合があるかと思います。この際、CloudFormationテンプレートに直接パスワードなどの機密情報を記述するのは、セキュリティ上のベストプラクティスに反します。
テンプレートはソースコードリポジトリに保存されることも多く、手違いで公開されてしまうとパスワードが流出するリスクもあるため、平文記述は極力避けたいです。
ということで、今回はRDSのパスワードをSecrets Managerで管理する構成を、CloudFormationテンプレートに記述してみたいと思います。
危ない例
こちらは危険なCloudFormationテンプレートの例です。パスワードが平文で記述されています。このテンプレートがパブリックな場所に置かれると、パスワードが流出してしまいます。
Resources: RDSDBInstance: Type: "AWS::RDS::DBInstance" Properties: DBInstanceIdentifier: sample-rds DBName: db DBInstanceClass: !Ref DBInstanceClass Engine: mysql EngineVersion: 8.0 Port: 3306 PubliclyAccessible: false AllocatedStorage: "20" MasterUsername: admin MasterUserPassword: password # パスワードが平文で保存されている!これは危ない! DBSubnetGroupName: !Ref SubnetGroup
セキュアな例
こちらはセキュアなCloudFormationテンプレートの例です。RDSのパスワードをSecrets Managetで管理してあるため、セキュリティをより確保しながらRDSを構築することができます。
Resources: RDSSecret: # ここでRDSのパスワードとなるランダム文字列を生成 Type: "AWS::SecretsManager::Secret" Properties: Name: "RDSSecret" Description: "RDS password for my RDS instance" GenerateSecretString: SecretStringTemplate: '{"username": "admin"}' GenerateStringKey: "password" PasswordLength: 16 ExcludeCharacters: '"@/\' RDSDBInstance: Type: "AWS::RDS::DBInstance" Properties: DBInstanceIdentifier: sample-rds DBName: db DBInstanceClass: !Ref DBInstanceClass Engine: mysql EngineVersion: 8.0 Port: 3306 PubliclyAccessible: false AllocatedStorage: "20" MasterUsername: admin MasterUserPassword: !Sub '{{resolve:secretsmanager:${MyRDSSecret}:SecretString:password}}' # Secrets Managerのランダム文字列を間接的に参照!これなら安心! DBSubnetGroupName: !Ref SubnetGroup
CloudFormationテンプレートのSecret Manger部分の詳細
さきほどのCloudFormationテンプレートのSecret Manger部分を再度示します。
Resources: RDSSecret: Type: "AWS::SecretsManager::Secret" Properties: Name: "MyRDSSecret" Description: "RDS password for my RDS instance" GenerateSecretString: SecretStringTemplate: '{"username": "admin"}' GenerateStringKey: "password" PasswordLength: 16 ExcludeCharacters: '"@/\'
SecretStringTemplate
の部分では、シークレットに含める項目をJSON文字列として定義しています。ここに記載の通り、生成されるシークレットには"username":"admin"
の項目が含まれることになります。
GenerateStringKey
の部分は、上記のSecretStringTemplate
で定義したJSONに追加されるキーを示します。
PasswordLength
の部分では、ランダムに生成するパスワードの長さを指定します。ここでは16
と指定していますので、16文字のランダムな文字列が生成されることになります。
ExcludeCharacters
の部分では、パスワード生成時に含めない文字を指定します。特定のシステムで特定の文字列が使えない、みたいな場合に、それらの文字を除外します。今回では、"
、@
、/
、\
の文字を除外しています。
これらの設定を踏まえると、16文字のランダムなパスワードが生成され、password
というキーにランダムパスワードが対応付けられます。そして、JSONに{"username": "admin"}
が追加されます。最終的なシークレットは、以下の通りになります。
{ "username": "admin", "password": "<16文字のランダムなパスワード>" }
CloudFormationテンプレートのRDS部分の詳細
さきほどのCloudFormationテンプレートのRDS部分を再度示します。
RDSDBInstance: Type: "AWS::RDS::DBInstance" Properties: DBInstanceIdentifier: sample-rds DBName: db DBInstanceClass: !Ref DBInstanceClass Engine: mysql EngineVersion: 8.0 Port: 3306 PubliclyAccessible: false AllocatedStorage: "20" MasterUsername: admin MasterUserPassword: !Sub '{{resolve:secretsmanager:${RDSSecret}:SecretString:password}}' DBSubnetGroupName: !Ref SubnetGroup
こちらの項目にて、Secrets Managerに保存しているシークレットを参照しています。
MasterUserPassword: !Sub '{{resolve:secretsmanager:${RDSSecret}:SecretString:password}}'
基本的には、RDSSecret
という名前でシークレットを指定しています。そして、このシークレットが持つpassword
という属性値をSecrets Managerから取得しています。取得したこの値は、RDSインスタンスのマスターユーザーのパスワードとして設定されます。
また、{{resolve:secretsmanager:${RDSSecret}:SecretString:password}}
の部分では、Secrets Managerのシークレット値を解決しています。secretsmanager
の後に続くARNによって、特定のシークレットを指定しています。そして、SecretString:password
の部分で、シークレットのJSON構造からパスワードを取得しているという感じです。
さいごに
RDSのパスワードをCloudFormationテンプレートで設定する際は、Secrets Managerを使うことで安全に管理できます。平文で保存することもないため、テンプレートからパスワードが漏れるといった心配もないですね。
本記事がお役に立てば幸いです。では!